/* Copyright (c) 2012, Roberto Javier Godoy.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Please contact Roberto Javier Godoy at <rjgodoy@fich.unl.edu.ar>,
 * <mail@rjgodoy.com.ar> if you need additional information or have any
 * questions.
 */
#include <iostream>
#include <string.h>

//include "Boolean.h"
#include "config.h"

#include "ParserEventGeneratorKit.h"

#include "ar_com_rjgodoy_sgml_sax_LocatorImpl.h"
#include "ar_com_rjgodoy_sgml_sax_SGMLReader.h"
using namespace std;

#ifdef SP_NAMESPACE
//using namespace SP_NAMESPACE;
#endif

typedef SGMLApplication::CharString CharString;

ostream &operator<<(ostream &os, CharString s)
{
  for (size_t i = 0; i < s.len; i++)
    os << char(s.ptr[i]);
  return os;
}
#ifdef SP_MULTI_BYTE  
jchar *newUnicodeChars(const CharString &s) {
jchar *unicodeChars = new jchar[s.len];
  for (size_t i = 0; i < s.len; i++) {
    if (s.ptr[i]>0xFFFF) {	
	 delete[] unicodeChars;	 
	 return 0;
	}
	unicodeChars[i]=s.ptr[i];
  }
  return unicodeChars;
}
#endif

jcharArray charArray (JNIEnv *env, const CharString &s) {
 jcharArray array = env->NewCharArray(s.len);
 if (!array) return 0;
 #ifdef SP_MULTI_BYTE  
  jchar *unicodeChars = newUnicodeChars(s);
  if (!unicodeChars) return 0;    
  env->SetCharArrayRegion(array, 0, s.len, unicodeChars);
  delete[] unicodeChars;  
 #else
  env->SetCharArrayRegion(array, 0, s.len, s.ptr);
 #endif
 return array;
}

jstring javastring(JNIEnv *env, const CharString &s) {
 jstring str;
 #ifdef SP_MULTI_BYTE  
  jchar *unicodeChars = newUnicodeChars(s);
  if (!unicodeChars) return 0;  
  str = env->NewString(unicodeChars,s.len);
  delete[] unicodeChars;
  return str;
 #else   
  str = env->NewStringUTF((const char *)s.ptr);
  return (jstring) env->NewWeakGlobalRef(str); 
 #endif  
}

jstring javastring(JNIEnv *env, const CharString *s) {
 return s?javastring(env,*s):0; 
}

//SGMLReader methods
jmethodID _getContentHandler;
jmethodID _getErrorHandler;
jmethodID _getAppinfoHandler;
jmethodID _getSgmlLexicalHandler;
jmethodID _getLexicalHandler;

//ContentHandler methods
jmethodID _startDocument, 
          _endDocument, 
		  _setDocumentLocator, 
		  _startElement, 
		  _endElement,
		  _characters, 
		  _processingInstruction;

//AppinfoHandler
jmethodID _appinfo;

//SgmlLexicalHandler
jmethodID _endProlog, _startMarkedSection, _endMarkedSection, _ignoredCharacters;

//LexicalHandler
jmethodID _endDTD, _startDTD, _startCDATA, _endCDATA, _comment;

//ErrorHandler
jmethodID _warning, _error, _fatalError;

//SAXParseException
jclass    saxParseExceptionClass;
jmethodID _newSAXParseException;

//LocatorImpl methods
jclass    locatorImplClass;
jmethodID _newLocatorImpl, _disposeLocator;

//MarkedSectionStatus enums
jobject _INCLUDE, _RCDATA, _CDATA, _IGNORE;

//Strings
jobject _STR_CDATA, _STR_TOKENIZED, _STR_ID, _STR_EMPTY;

//Attributes2Impl
jclass    attributes2ImplClass;
jmethodID  _newAttributes2Impl,
           _addAttribute,
           _clear,
		   _setDeclared,
		   _setSpecified;


/*
 * Class:     ar_com_rjgodoy_sgml_sax_SGMLReader
 * Method:    init0
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_ar_com_rjgodoy_sgml_sax_SGMLReader_init0
  (JNIEnv *env, jclass) {

 jclass cl;
 
 #define CLASS(N) do {\
     jclass local_cl = env->FindClass(N);\
	 if (!local_cl) return;\
	 cl=(jclass)env->NewGlobalRef(local_cl);\
	 env->DeleteLocalRef(local_cl);\
 } while(0)
 
 #define METHOD(M,S) do {\
     _ ## M = env->GetMethodID(cl, #M, S);\
     if (! _ ## M) return;\
 } while (0)

 #define CONSTRUCTOR(C,P) do {\
    _ ## C=env->GetMethodID(cl,"<init>",(P));\
    if (!_ ## C) return;\
 } while (0)
 
 #define ENUM_FIELD(VALUE_OF,F) do{\
   jobject enum_value = env->CallStaticObjectMethod(cl,VALUE_OF,env->NewStringUTF(#F));\
   if (env->ExceptionOccurred()) {\
    env->ExceptionClear();\
	CLASS("java/lang/NoSuchFieldError");\
    jclass noSuchFieldErrorClass=cl;\
	env->ThrowNew(noSuchFieldErrorClass,#F);\
	return;\
   }\
   _ ## F = env->NewGlobalRef(enum_value);\
	env->DeleteLocalRef(enum_value);\
  } while (0)  
  
  CLASS("ar/com/rjgodoy/sgml/sax/SGMLReader");
  METHOD(getContentHandler,"()Lorg/xml/sax/ContentHandler;");
  METHOD(getErrorHandler,"()Lorg/xml/sax/ErrorHandler;");
  METHOD(getLexicalHandler,"()Lorg/xml/sax/ext/LexicalHandler;"); 
  METHOD(getAppinfoHandler,"()Lar/com/rjgodoy/sgml/sax/AppinfoHandler;");
  METHOD(getSgmlLexicalHandler,"()Lar/com/rjgodoy/sgml/sax/SGMLLexicalHandler;"); 
  
  CLASS("org/xml/sax/ContentHandler");
  METHOD(startDocument,"()V"); 
  METHOD(endDocument,"()V"); 
  METHOD(setDocumentLocator,"(Lorg/xml/sax/Locator;)V");  
  METHOD(startElement,"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/xml/sax/Attributes;)V");
  METHOD(endElement,"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
  METHOD(characters,"([CII)V");
  METHOD(processingInstruction,"(Ljava/lang/String;Ljava/lang/String;)V");
  
  CLASS("org/xml/sax/ErrorHandler");
  METHOD(warning, "(Lorg/xml/sax/SAXParseException;)V");
  METHOD(error, "(Lorg/xml/sax/SAXParseException;)V");
  METHOD(fatalError, "(Lorg/xml/sax/SAXParseException;)V");
  
  CLASS("org/xml/sax/SAXParseException"); saxParseExceptionClass=cl;
  CONSTRUCTOR(newSAXParseException,"(Ljava/lang/String;Lorg/xml/sax/Locator;)V");
  
  CLASS("ar/com/rjgodoy/sgml/sax/AppinfoHandler");  
  METHOD(appinfo,"(Ljava/lang/String;)V");
  
  CLASS("ar/com/rjgodoy/sgml/sax/SGMLLexicalHandler"); 
  METHOD(endProlog,"()V");
  METHOD(startMarkedSection,"(Lar/com/rjgodoy/sgml/sax/MarkedSectionStatus;Z)V");
  METHOD(endMarkedSection,"(Lar/com/rjgodoy/sgml/sax/MarkedSectionStatus;)V");
  METHOD(ignoredCharacters,"([CII)V");
  
  CLASS("org/xml/sax/ext/LexicalHandler"); 
  METHOD(startDTD,"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");  
  METHOD(endDTD,"()V");  
  METHOD(startCDATA,"()V");
  METHOD(endCDATA,"()V");
  METHOD(comment,"([CII)V");
  
  
  CLASS("ar/com/rjgodoy/sgml/sax/LocatorImpl"); locatorImplClass=cl;
  METHOD(disposeLocator,"()V");
  CONSTRUCTOR(newLocatorImpl,"(J)V");
 
  
  CLASS("ar/com/rjgodoy/sgml/sax/MarkedSectionStatus");  
  
  jmethodID _valueOf = env->GetStaticMethodID(cl, "valueOf", "(Ljava/lang/String;)Lar/com/rjgodoy/sgml/sax/MarkedSectionStatus;");
  if (!_valueOf) return;

  ENUM_FIELD(_valueOf,CDATA);
  ENUM_FIELD(_valueOf,INCLUDE);
  ENUM_FIELD(_valueOf,RCDATA);
  ENUM_FIELD(_valueOf,IGNORE);

  CLASS("org/xml/sax/ext/Attributes2Impl"); attributes2ImplClass=cl;
  CONSTRUCTOR(newAttributes2Impl,"()V");
  METHOD(addAttribute,"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
  METHOD(clear,"()V");
  METHOD(setDeclared,"(IZ)V");
  METHOD(setSpecified,"(IZ)V");
  
  _STR_CDATA     = env->NewGlobalRef (env->NewStringUTF("CDATA"));
  _STR_TOKENIZED = env->NewGlobalRef (env->NewStringUTF("TOKENIZED"));
  _STR_ID        = env->NewGlobalRef (env->NewStringUTF("ID"));
  _STR_EMPTY     = env->NewGlobalRef (env->NewStringUTF(""));
 
  #undef CLASS
  #undef METHOD
  #undef ENUM_FIELD
}

#define JAVACALL(OBJECT,METHOD,...) do {\
  if((OBJECT))\
    env->CallVoidMethod(OBJECT,_ ## METHOD, ##__VA_ARGS__);\
  } while (0)

#define JAVASTRING(VARIABLE,VALUE) \
	 jobject VARIABLE= javastring(env,(VALUE));\
	 if (!VARIABLE) return character_error()\

//test if handler is defined and set position pointer
#define EVENT_ENTER(HANDLER,POSITION) do{\
    if (!HANDLER) return;\
	pos = POSITION;\
  } while (0) 
	
//reset position pointer and halt parsing if an exception occurred
#define EVENT_LEAVE do{\
	pos = 0;\
	if (env->ExceptionOccurred()) {egp->halt();failed=true;}\
  } while (0)
  
class SGMLApplicationImpl : public SGMLApplication {
private: 
  const Position *pos;
  OpenEntityPtr openEntityPtr;
  
  JNIEnv *env;
  const jobject obj;
  
public:
  bool failed;  
     
  jobject contentHandler;
  jobject errorHandler;
  jobject appinfoHandler;
  jobject sgmlLexicalHandler;
  jobject lexicalHandler;
  jobject locator;
  jobject attributes;
  EventGenerator *egp;
  
  SGMLApplicationImpl(JNIEnv *_env,EventGenerator *_egp,jobject _obj) : 
	failed(0),
	locator(0),
	obj(_obj),
	env(_env),	 
	egp(_egp)
  {
    updateHandlers(env);
	this->attributes = env->NewObject(attributes2ImplClass,_newAttributes2Impl);
  }
    
  ~SGMLApplicationImpl() {
    if (locator)
      JAVACALL(locator,disposeLocator);
  }
  
  Location location() {   
   if (pos) {
    return openEntityPtr->location(*pos);
   } else {
    return Location();
   }
  }
 
  void character_error() {
  
  }
  
  void updateHandlers(JNIEnv *env) {
    contentHandler     = env->CallObjectMethod(obj,_getContentHandler);
    errorHandler       = env->CallObjectMethod(obj,_getErrorHandler);
    appinfoHandler     = env->CallObjectMethod(obj,_getAppinfoHandler);
    sgmlLexicalHandler = env->CallObjectMethod(obj,_getSgmlLexicalHandler);    
	lexicalHandler     = env->CallObjectMethod(obj,_getLexicalHandler);    
	if (locator&&contentHandler) JAVACALL(contentHandler,setDocumentLocator,locator);
  }
  	 
  void startElement(const StartElementEvent &e) {
    EVENT_ENTER(contentHandler,&e.pos);	
	JAVACALL(attributes,clear);
	for (int i=0;i<e.nAttributes;i++) {
	   const Attribute &attr = e.attributes[i];
	   JAVASTRING(name,attr.name);	  
	   jobject type;	   
	   jobject value;
	   
	   switch (attr.type) {
		  case Attribute::cdata: {
		    CharString data;			
			data.len=0;
			for (int j=0;j<attr.nCdataChunks;j++) data.len+=attr.cdataChunks[j].data.len;
			Char* ptr=new Char[data.len];
			data.ptr=ptr;
			data.len=0;
			for (int j=0;j<attr.nCdataChunks;j++) {		
			 const CharString &chunk = attr.cdataChunks[j].data;
			 memcpy(&ptr[data.len],chunk.ptr,sizeof(Char)*chunk.len);
			 data.len+=chunk.len;
			}
			JAVASTRING(str,data);
			type=_STR_CDATA;
			value=str;
			break;
		  }
		  case Attribute::tokenized: {
		    JAVASTRING(str,attr.tokens);
			type=_STR_TOKENIZED;
			value=str;
			if (attr.isId)    type=_STR_ID;
			break;
		  }
		  default: continue;
	   }	   
	   JAVACALL(attributes,addAttribute,_STR_EMPTY,_STR_EMPTY,name,type,value);
	   
	   switch (attr.defaulted) {
	     case Attribute::specified: 
		  break;
		 case Attribute::definition:
		  break;
		 case Attribute::current:
		  //defaulted from current value
		  //specified=false declared=true current=true 
		  break;
	   }
	}
	JAVASTRING(name,e.gi);	
	
	JAVACALL(contentHandler,startElement,_STR_EMPTY,_STR_EMPTY,name,attributes);	
	EVENT_LEAVE;
  }
 
  void endElement(const EndElementEvent &e) {
    EVENT_ENTER(contentHandler,&e.pos);
	JAVASTRING(name,e.gi);
	JAVACALL(contentHandler,endElement,_STR_EMPTY,_STR_EMPTY,name);	
	EVENT_LEAVE;
  }
  
  void appinfo(const AppinfoEvent &e) {	
    EVENT_ENTER(appinfoHandler,&e.pos);
	if (e.none) {
	  JAVACALL(appinfoHandler,appinfo,0);
	} else {
	 JAVASTRING(value,e.string);
	 JAVACALL(appinfoHandler,appinfo,value);
	}
	EVENT_LEAVE;
  }
  
  void startDtd(const StartDtdEvent &e) {
    EVENT_ENTER(lexicalHandler,&e.pos);
	
    const CharString* systemId = 0;
	const CharString* publicId = 0;
	if (e.haveExternalId&&e.externalId.haveSystemId) systemId=&e.externalId.systemId;
	if (e.haveExternalId&&e.externalId.havePublicId) publicId=&e.externalId.publicId;
		
	JAVASTRING(name,e.name);	
	JAVACALL(lexicalHandler,startDTD,
	         name,
			 javastring(env,publicId),
			 javastring(env,systemId));
	EVENT_LEAVE;
  }
  
  void endDtd(const EndDtdEvent &e) {	
    EVENT_ENTER(lexicalHandler,&e.pos);
	JAVACALL(lexicalHandler,endDTD);
	EVENT_LEAVE;
  }	  
  
  void endProlog(const EndPrologEvent &e) {
    EVENT_ENTER(sgmlLexicalHandler,&e.pos);
	JAVACALL(sgmlLexicalHandler,endProlog);	
	EVENT_LEAVE;
  }

  void data(const DataEvent &e) {
    EVENT_ENTER(contentHandler,&e.pos);
	jcharArray array = charArray(env,e.data);
	if (!array) return;
	JAVACALL(contentHandler,characters,array,0,e.data.len);
	EVENT_LEAVE;
  }
  
  void ignoredChars(const IgnoredCharsEvent &e) {
    EVENT_ENTER(sgmlLexicalHandler,&e.pos);
	jcharArray array = charArray(env,e.data);
	if (!array) return;
	JAVACALL(sgmlLexicalHandler,ignoredCharacters,array,0,e.data.len);
	EVENT_LEAVE;
 }
 
  void commentDecl(const CommentDeclEvent &e) {
    EVENT_ENTER(lexicalHandler,&e.pos);
	if (e.nComments==0) {	  
	  jcharArray array = env->NewCharArray(0);      
	  if (!array) return;
      JAVACALL(lexicalHandler,comment,array,0,0);
    } else for (int i=0;i<e.nComments;i++) {	
	  jcharArray array = charArray(env,e.comments[i]);
	  if (!array) return;
      JAVACALL(lexicalHandler,comment,array,0,e.comments[i].len);	
	}	  
	EVENT_LEAVE;
  }
  
  void pi(const PiEvent &e) {
    EVENT_ENTER(contentHandler,&e.pos);
	JAVASTRING(data,e.data);	
	JAVACALL(contentHandler,processingInstruction,0,data);
	EVENT_LEAVE;
  } 
    
  void sdata(const SdataEvent &e) {
	//TODO
	cout<<"sdata"<<endl;
	EVENT_ENTER(0,&e.pos);
	EVENT_LEAVE;
  }
  
  void externalDataEntityRef(const ExternalDataEntityRefEvent &e) {
	//TODO
	cout<<"externalDataEntityRef"<<endl;
	EVENT_ENTER(0,&e.pos);
	EVENT_LEAVE;
  }
  
  void subdocEntityRef(const SubdocEntityRefEvent &e) {
	//TODO
	cout<<"subdocEntityRef"<<endl;
	EVENT_ENTER(0,&e.pos);	
	EVENT_LEAVE;
  }
  
  void nonSgmlChar(const NonSgmlCharEvent &e) {
	//TODO
	cout<<"nonSgmlChar"<<endl;
	EVENT_ENTER(0,&e.pos);
	EVENT_LEAVE;
  }
  
  void markedSectionStart(const MarkedSectionStartEvent &e) {    
	bool is_cdata = e.status==MarkedSectionStartEvent::cdata;
	EVENT_ENTER(sgmlLexicalHandler||is_cdata&&lexicalHandler,&e.pos);
	
	bool temp=false;
	for (int i=0;i<e.nParams&&!temp;i++) {
	  temp |= e.params[i].type==MarkedSectionStartEvent::Param::temp;
	}
	jobject status = 0;
	switch (e.status) {
	  case MarkedSectionStartEvent::cdata  : status=_CDATA;   break;
	  case MarkedSectionStartEvent::rcdata : status=_RCDATA;  break;
	  case MarkedSectionStartEvent::ignore : status=_IGNORE;  break;
	  case MarkedSectionStartEvent::include: status=_INCLUDE; break;
	}
	JAVACALL(sgmlLexicalHandler,startMarkedSection,status,temp);	  
	if (is_cdata) {
	  JAVACALL(lexicalHandler,startCDATA);
	}
	EVENT_LEAVE;
  }
  
  void markedSectionEnd(const MarkedSectionEndEvent &e) {
    bool is_cdata = e.status==MarkedSectionEndEvent::cdata;
	EVENT_ENTER(sgmlLexicalHandler||is_cdata&&lexicalHandler,&e.pos);
	
	if (is_cdata) {
	  JAVACALL(lexicalHandler,endCDATA);
	}
	jobject status = 0;
	switch (e.status) {
	  case MarkedSectionEndEvent::cdata  : status=_CDATA;   break;
	  case MarkedSectionEndEvent::rcdata : status=_RCDATA;  break;
	  case MarkedSectionEndEvent::ignore : status=_IGNORE;  break;
	  case MarkedSectionEndEvent::include: status=_INCLUDE; break;
	}
	JAVACALL(sgmlLexicalHandler,endMarkedSection,status);	  
	EVENT_LEAVE;
  }
  
  void generalEntity(const GeneralEntityEvent &e) {
    //TODO
	//cout<<"generalEntity "<<e.entity.name<<endl;
  }
  
  void error(const ErrorEvent &e) {
    EVENT_ENTER(errorHandler,&e.pos);
   
    JAVASTRING(message,e.message);
	jobject spe = env->NewObject(saxParseExceptionClass,_newSAXParseException,message,locator);
	spe=env->NewWeakGlobalRef(spe);
	  
	switch (e.type) {
	  case ErrorEvent::info:
	   cout<<"[INFO] "<<e.message<<endl;  	   
	   break;
	  case ErrorEvent::warning:
	   JAVACALL(errorHandler,warning,spe);
	   break;
	  case ErrorEvent::otherError:
	   JAVACALL(errorHandler,fatalError,spe);
	   break;
	  default:
	   JAVACALL(errorHandler,error,spe);
	}
	
	EVENT_LEAVE;
  }  
 
 void openEntityChange(const OpenEntityPtr &ptr) {    
  this->openEntityPtr=ptr;      
  if (!locator) {   
   locator = env->NewObject(locatorImplClass,_newLocatorImpl,(jlong)this);
   locator = env->NewWeakGlobalRef(locator);   
   JAVACALL(contentHandler,setDocumentLocator,locator);
   JAVACALL(contentHandler,startDocument);    
  }  
 }

};

/*
 * Class:     ar_com_rjgodoy_sgml_sax_SGMLReader
 * Method:    parse0
 * Signature: (Ljava/lang/String;Ljava/lang/String;)V
 */
void JNICALL Java_ar_com_rjgodoy_sgml_sax_SGMLReader_parse0
  (JNIEnv *env, jobject obj, jstring j_sgmldecl, jstring j_systemid) 
{
 ParserEventGeneratorKit parserKit;
 EventGenerator *egp;
 
 parserKit.setOption(ParserEventGeneratorKit::outputCommentDecls);
 parserKit.setOption(ParserEventGeneratorKit::outputMarkedSections);
 parserKit.setOption(ParserEventGeneratorKit::outputGeneralEntities);
 parserKit.setOption(ParserEventGeneratorKit::showOpenEntities);
 parserKit.setOption(ParserEventGeneratorKit::showOpenElements);
 parserKit.setOption(ParserEventGeneratorKit::showErrorNumbers);
 //ParserEventGeneratorKit::enableWarning, ... This corresponds to the -w option of nsgmls.
 
 //makeEventGenerator
 const char *sgmldecl = j_sgmldecl?env->GetStringUTFChars(j_sgmldecl, 0):0;
 const char *systemid = env->GetStringUTFChars(j_systemid, 0); 
 if (sgmldecl) { 
    char **files = new char*[2];
	files[0] = new char[strlen(sgmldecl)+1]; strcpy(files[0],sgmldecl);
	files[1] = new char[strlen(systemid)+1]; strcpy(files[1],systemid);
	egp=parserKit.makeEventGenerator(2, files);
	delete[] files[0];
	delete[] files[1];
	delete[] files;
 }  else {    
    char **files = new char*[1];
	files[0] = new char[strlen(systemid)+1]; strcpy(files[0],systemid);
	egp=parserKit.makeEventGenerator(1, files);
	delete[] files[0];
	delete[] files;
 }   
  egp->inhibitMessages(true);
    
  SGMLApplicationImpl app(env,egp,obj);   
  
  egp->run(app);

  if (!app.failed) 
    JAVACALL(app.contentHandler,endDocument);  
 
  delete egp;  
}

/*
 * Class:     ar_com_rjgodoy_sgml_sax_LocatorImpl
 * Method:    getColumnNumber0
 * Signature: (J)I
 */
jint JNICALL Java_ar_com_rjgodoy_sgml_sax_LocatorImpl_getColumnNumber0
  (JNIEnv *, jclass, jlong ptr) 
{
 return ((SGMLApplicationImpl*)ptr)->location().columnNumber;
}

/*
 * Class:     ar_com_rjgodoy_sgml_sax_LocatorImpl
 * Method:    getLineNumber0
 * Signature: (J)I
 */
jint JNICALL Java_ar_com_rjgodoy_sgml_sax_LocatorImpl_getLineNumber0
  (JNIEnv *, jclass, jlong ptr) 
{
 return ((SGMLApplicationImpl*)ptr)->location().lineNumber;  
}

/*
 * Class:     ar_com_rjgodoy_sgml_sax_LocatorImpl
 * Method:    getSystemId0
 * Signature: (J)Ljava/lang/String;
 */
jstring JNICALL Java_ar_com_rjgodoy_sgml_sax_LocatorImpl_getSystemId0
  (JNIEnv *env, jclass, jlong ptr) 
{
  CharString cs = ((SGMLApplicationImpl*)ptr)->location().filename;    
  jstring js = javastring(env,cs);
  return js;
}
  
/*
 * Class:     ar_com_rjgodoy_sgml_sax_SGMLReader
 * Method:    updateHandlers0
 * Signature: (J)V
 */
JNIEXPORT void JNICALL Java_ar_com_rjgodoy_sgml_sax_SGMLReader_updateHandlers0
  (JNIEnv *env, jobject obj, jlong ptr) 
{
  ((SGMLApplicationImpl*)ptr)->updateHandlers(env);
}

JNIEXPORT jint JNICALL JNI_OnLoad
  (JavaVM *vm, void *reserved) 
{
  return JNI_VERSION_1_2;
}
